﻿// Shader-Based-Image-Processing (SBIP)
// http://code.google.com/p/sbip/
//
// Copyright © Frank Nagl, 2009-2011
// admin@franknagl.de
//
namespace SBIP.Controls
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Windows.Forms;
    using SBIP.Helper;
    using SBIP.Helper.DataTypes;
    using SBIP.Helper.Enums;

    /// <summary>
    /// Control to display an image for sketching figures in it.
    /// </summary>
    /// <remarks>
    /// <para>
    /// Into the shown image the user can sketch closed figures in it easily by
    /// using the mouse.
    /// </para>
    /// <para>
    /// Left mouse button: Setting a new corner point.
    /// </para>
    /// <para>
    /// Middle mouse button or ESC key: Removing last figure.
    /// </para>
    /// <para>
    /// Right mouse button: Closing a figure.
    /// </para>
    /// </remarks>
    public class ImageSketchControl : Control
    {
        // Minimum number of corners for a polygon
        private const int MIN = 3;

        private readonly Figure currenLineFig;
        private FigurePoint? fixedPoint;
        private bool usingFixPoints;
        private FigureType usedFigureType;
        private FigurePoint? s1;
        // Current Id
        private int Cid { get {return Figures.Count - 1;} }

        /// <summary> All closed polygons of the image. </summary>
        public List<Figure> Figures { get; set; }


        /// <summary>
        /// Initializes a new instance of the <see cref="ImageSketchControl"/> 
        /// class.
        /// </summary>
        /// <param name="image">The image to use.</param>
        public ImageSketchControl(Image image)
        {
            BackgroundImage = image;
            MouseDown += MouseDownEvent;            
            MouseMove += MouseMoveEvent;
            KeyDown += KeyDownEvent;
            KeyUp += KeyUpEvent;
            usedFigureType = FigureType.Common;
            currenLineFig = new Figure(0, FigureType.Line);
            Figures = new List<Figure>();
            DoubleBuffered = true;       
        }

        private void KeyDownEvent(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Escape:
                    OnMouseDown(new MouseEventArgs(
                        MouseButtons.Middle, 1, 0, 0, 0));
                    break;

                case Keys.ControlKey:
                    usingFixPoints = true;
                    break;

                case Keys.D1:
                    usedFigureType = FigureType.Common;
                    Console.WriteLine(@"Setting common figure type.");
                    break;

                case Keys.D2:
                    usedFigureType = FigureType.Orthogonal;
                    Console.WriteLine(@"Setting orthogonal figure type.");
                    break;

                case Keys.D3:
                    usedFigureType = FigureType.Background;
                    Console.WriteLine(@"Setting background figure type.");
                    break;
            }
        }

        private void KeyUpEvent(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.ControlKey:
                    usingFixPoints = false;
                    Invalidate();
                    break;
            }
        }

        private void MouseDownEvent(object sender, MouseEventArgs e)
        {
            switch(e.Button)
            {
                case MouseButtons.Left:
                    if (s1 == null)
                    {
                        Figures.Add(new Figure(Figures.Count, usedFigureType));
                    }

                    if (usingFixPoints && fixedPoint != null)
                    {
                         //Figures[Cid].SharedCorners.Add(
                         //       new FigurePoint(Figures.Count - 1, fixedPoint.ValueZ));

                        // s1 = fixedPoint;
                        s1 = new FigurePoint(
                            fixedPoint.Value, 
                            Figures[Cid].Corners.Count,
                            Figures[Cid].Id);
                    }
                    else
                    {
                        s1 = new FigurePoint(
                            e.Location.X, 
                            e.Location.Y, 
                            Figures[Cid].Corners.Count, 
                            Figures[Cid].Id);
                    }
                    
                    Figures[Cid].AddCorner(s1.Value);
                    Console.WriteLine(s1.Value.ToPoint());
                    break;

                case MouseButtons.Middle:
                    // SendKeys.Send("{ESC}");
                        currenLineFig.Reset();
                        s1 = null;
                    if (Cid != -1)
                    {
                        Figures.RemoveAt(Cid);
                    }
                    break;

                case MouseButtons.Right:
                    currenLineFig.Reset();
                    s1 = null;
                    if (Cid != -1 && Figures[Cid].Corners.Count < MIN)
                    {
                        Figures.RemoveAt(Cid);
                    }
                    break;
            }
            Invalidate();
        }

        private void MouseMoveEvent(object sender, MouseEventArgs e)
        {
            Invalidate();
            fixedPoint = usingFixPoints ? NearLocation(e.Location) : null;    

            if (s1 == null)
            {
                return;
            }
            currenLineFig.Reset();
            currenLineFig.AddCorner(s1.Value);

            if (usingFixPoints && fixedPoint != null)
            {
                currenLineFig.AddCorner(fixedPoint.Value);                
            }
            else
            {
                currenLineFig.AddCorner(new FigurePoint(
                                                e.Location.X, 
                                                e.Location.Y, 
                                                currenLineFig.Corners.Count, 
                                                currenLineFig.Id)); 
            }
            
        }

        private FigurePoint? NearLocation(Point p)
        {
            const int Tol = 10;
            float dist = float.MaxValue;
            FigurePoint? bestMatch = null;
            foreach (Figure f in Figures)
            {
                foreach (FigurePoint fp in f.Corners)
                {
                    if (p.X - Tol < fp.X && p.X + Tol > fp.X &&
                        p.Y - Tol < fp.Y && p.Y + Tol > fp.Y)
                    {
                        float tmp = Vector.Distance(fp.ToPoint(), p);
                        if (tmp < dist)
                        {
                            dist = tmp;
                            bestMatch = fp;
                        }
                    }
                }
            }
            return bestMatch;
        }

        /// <summary>
        /// Fires the <see cref="E:System.Windows.Forms.Control.Paint"/>-Event.
        /// </summary>
        /// <param name="e">An instance of 
        /// <see cref="T:System.Windows.Forms.PaintEventArgs"/>.</param>
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
            //DrawingGraphics.DrawCross(
            //   e.Graphics, new Point(50, 50), Color.GreenYellow, 2, 10);

            foreach (Figure f in Figures)
            {
                f.Draw(e.Graphics);
                f.Fill(e.Graphics);
            }
            currenLineFig.Draw(e.Graphics);
            if (usingFixPoints && fixedPoint != null)
            {
                DrawingGraphics.DrawCross(
                    e.Graphics, fixedPoint.Value.ToPoint(), Color.GreenYellow, 2, 10);
            }
        }
    }
}
